package com.pilipala.service.Impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.pilipala.constant.*;
import com.pilipala.context.BaseContext;
import com.pilipala.dto.*;
import com.pilipala.entity.User;
import com.pilipala.exception.EmailException;
import com.pilipala.exception.LoginException;
import com.pilipala.exception.UploadException;
import com.pilipala.mapper.UserMapper;
import com.pilipala.properties.JwtProperty;
import com.pilipala.result.PageResult;
import com.pilipala.service.UserService;
import com.pilipala.utils.AliOssUtil;
import com.pilipala.utils.JwtUtil;
import com.pilipala.vo.UserLoginVO;
import com.pilipala.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.validator.routines.EmailValidator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;

@Service
@Slf4j
public class UserServiceImpl implements UserService {
//    邮箱校验器
    @Autowired
    private EmailValidator emailValidator;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private JwtProperty jwtProperty;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private AliOssUtil aliOssUtil;

    /**
     * 用于登录
     * @param userLoginDTO 包含type,email,password,code
     * @return 包含token
     */
    public UserLoginVO login(UserLoginDTO userLoginDTO) {
//        检验email是否合法
        String email = userLoginDTO.getEmail();
        if (!emailValidator.isValid(email)) {
//              邮箱不合法   直接报错
            throw new EmailException(LoginConstant.EMAIL_IS_NOT_VALID);
        }


//        合法邮箱就查看邮箱是否被注册过
        User user = userMapper.getByEmail(email);
        String code = (String) redisTemplate.opsForValue().get("email:"+email);
        String token;


        if (userLoginDTO.getType().equals(LoginConstant.EMAIL)){
//            如果是邮箱登录 查询是否存在用户
            if (code == null){
//                屏蔽掉没有发送验证码就登陆的
                throw new EmailException(LoginConstant.CODE_NOT_EXIT);
            }
            else if (code.equals(userLoginDTO.getCode())){
//                    验证码正确发送token
                    Map<String,Object> claims = new HashMap<>();
//                    就放一个id
                    if (user == null){
                        user = new User();
                        BeanUtils.copyProperties(userLoginDTO,user);
//                        随机uuid密码
                        user.setPassword(UUID.randomUUID().toString());
//                        要返回主键
                        userMapper.insert(user);
                    }
                    claims.put(JwtConstant.USER_ID,user.getId());
                    token = jwtUtil.generateToken(jwtProperty.getSecretKey(), jwtProperty.getTtl(), claims);
//                    验证完一次要让验证码失效
                redisTemplate.delete("email:"+email);
            }

            else {
//                  验证码错误直接报错
                throw new EmailException(LoginConstant.CODE_IS_NOT_RIGHT);
            }

        }

        else{
//            如果是密码登录直接校验密码
            String md5 = DigestUtils.md5DigestAsHex(userLoginDTO.getPassword().getBytes());
            if (Objects.equals(md5,user.getPassword())){
//                密码正确
                Map<String,Object> claims = new HashMap<>();
                claims.put(JwtConstant.USER_ID,user.getId());
                token = jwtUtil.generateToken(jwtProperty.getSecretKey(), jwtProperty.getTtl(), claims);
            }
            else {
//                密码错误
                throw new LoginException(LoginConstant.PASSWORD_NOT_RIGHT);
            }
        }


        return UserLoginVO.builder().token(token).build();
    }

    /**
     * 更新用户
     * @param userDTO 包含用户所有信息
     */
    @Override
    public void update(UserDTO userDTO) {
//        创建实体
        User user = new User();
        BeanUtils.copyProperties(userDTO,user);
        userMapper.update(user);
    }


    /**
     * 更新用户密码
     * @param privateDTO 密码、邮箱、验证码
     */
    @Override
    public void updatePrivate(UserPrivateDTO privateDTO) {
//        获取验证码
        log.info("更新邮箱和密码:{}",privateDTO);
        String code = (String) redisTemplate.opsForValue().get("email:"+privateDTO.getEmail());
        if (code == null){
//            邮箱没有验证码
            throw new EmailException(CodeConstant.CODE_NOT_EXIT);
        }
        if (privateDTO.getCode() == null){
//            没填写验证码
            throw new EmailException(CodeConstant.CODE_NOT_SEND);
        }
        if (code.equals(privateDTO.getCode())){
//            验证码正确
//            更新用户email或者账户密码
            User user = new User();
            BeanUtils.copyProperties(privateDTO,user);
            if (user.getPassword() != null){
//                修改密码的话就要加密
                user.setPassword(
                        DigestUtils.md5DigestAsHex(user.getPassword().getBytes())
                );
            }
//            想要更新邮箱但是邮箱不合法
            if (user.getEmail() != null && !emailValidator.isValid(privateDTO.getEmail())){
                    throw new EmailException(LoginConstant.EMAIL_IS_NOT_VALID);
            }
            userMapper.update(user);
//            删除验证码
            redisTemplate.delete("email:"+privateDTO.getEmail());
        }
    }

    @Override
    public UserVO getUserById(Long id) {
        UserIdDTO userIdDTO = new UserIdDTO(BaseContext.getCurrentId(),id);
        UserVO user = userMapper.getUserByIdWithFollow(userIdDTO);
//        加密密码
        if (user != null){
            user.setPassword("**********");
        }
        return user;
    }

    /**
     * 上传用户头像
     * @param file 图片文件
     */
    @Override
    public void uploadAvatar(MultipartFile file) {
        try{
//            获取到完整的文件名称
            String originalFilename = file.getOriginalFilename();
//            获取到文件后缀名
            String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
            String objectName = UploadConstant.USER_AVATAR_PRE + UUID.randomUUID().toString() + extension;
//            上传文件
            String filePath = aliOssUtil.upload(file.getBytes(), objectName);
//            固定到持久化层
            Long id = BaseContext.getCurrentId();
            User user = User.builder().id(id).avatarPath(filePath).build();
            userMapper.update(user);
        } catch (IOException e) {
            throw new UploadException(MessageConstant.UPLOAD_FAIL);
        }
    }

    /**
     * 关于用户的搜索接口
     * @param searchDTO 包括页码、页大小、搜索关键字、排序类型
     *                  在这里排序类型失效
     * @return 返回分页后的用户Id
     *          注意了这里只查询Id
     */
    @Override
    public PageResult search(SearchDTO searchDTO) {
        PageHelper.startPage(searchDTO.getPage(),searchDTO.getPageSize());
        Page<Long> page = userMapper.search(searchDTO);

        long total = page.getTotal();
        List<Long> userList = page.getResult();

        page.close();
        PageHelper.clearPage();
        return new PageResult(total,userList);
    }

    /**
     * 关注用户
     * @param followedId 被关注人的Id
     */
    @Override
    public void follow(Long followedId) {
        UserIdDTO userIdDTO = new UserIdDTO(BaseContext.getCurrentId(),followedId);
        Long followsId = userMapper.get(userIdDTO);

        User followedUser = userMapper.getUserById(followedId);
        User followerUser = userMapper.getUserById(BaseContext.getCurrentId());
//        没关注，那关注一下
        if (followsId == null) {
            userMapper.follow(userIdDTO);
            followedUser.setFans(followedUser.getFans() + 1);
            followedUser.setFollows(null);

            followerUser.setFollows(followerUser.getFollows() + 1);
            followerUser.setFans(null);
        }
//          有这个那就是取关事件
        else{
            userMapper.unfollow(followsId);
            followedUser.setFans(followedUser.getFans() - 1);
            followedUser.setFollows(null);

            followerUser.setFollows(followerUser.getFollows() - 1);
            followerUser.setFans(null);
        }

//        关注者和被关注者都要更新信息
        userMapper.update(followedUser);

        userMapper.update(followerUser);
    }


    /**
     * 获取关注人的Id
     * @param searchDTO 有效数据只有页码和页大小
     * @return 获取到分页后的数据
     */
    @Override
    public PageResult getFollowed(SearchDTO searchDTO) {
        Long followerId = BaseContext.getCurrentId();
        PageHelper.startPage(searchDTO.getPage(),searchDTO.getPageSize());
        Page<Long> page = userMapper.getFollowed(followerId);

        long total = page.getTotal();
        List<Long> idList = page.getResult();

        return new PageResult(total,idList);
    }
}
